﻿using System;
using System.Collections.Generic;
using System.IO;

namespace Me.Prettyprint.Cassandra.Utils
{
    /// <summary>
    /// ByteBuffer is a basic wrapper around byte[] array. It isn't equal to
    /// Java's ByteBuffer. The class doesn't extend C# MemoryStream, since
    /// the class wasn't designed to be extended. Many of the fields in
    /// MemoryStream aren't accessible, so we can't get the at the raw
    /// byte[] in MemoryStream.
    /// </summary>
    public class ByteBuffer : IComparable<ByteBuffer>
    {
        public ByteBuffer(byte[] bytes)
        {
            ReadableBytes = bytes;
            Length = bytes.Length;
            Position = 0;
            Limit = bytes.Length;
        }
        public ByteBuffer()
            : base()
        {
        }
        public ByteBuffer(int capacity) : this (new byte[capacity])
        {
        }
        public ByteBuffer(byte[] bytes, int offset)
            : this(bytes)
        {
            Offset = offset;
            Limit = bytes.Length;
        }
        public int Length
        {
            get
            {
                if (Limit < ReadableBytes.Length)
                {
                    return Limit;
                }
                else
                {
                    return ReadableBytes.Length;
                }
            }
            set
            {
                if (value > Length)
                {
                    byte[] newarray = new byte[value];
                    Array.Copy(ReadableBytes, newarray, ReadableBytes.Length);
                    ReadableBytes = newarray;
                }
            }
        }
        public int Position
        {
            get;
            set;
        }
        public int Limit
        {
            get;
            set;
        }
        protected int HashCode
        {
            get;
            set;
        }
        protected byte[] ReadableBytes
        {
            get;
            set;
        }
        public byte[] GetBuffer()
        {
            return ReadableBytes;
        }
        public int Offset
        {
            get;
            set;
        }
        public long Remaining()
        {
            return Length - Position;
        }
        public bool HasRemaining
        {
            get
            {
                return (Remaining() > 0);
            }
        }
        public void Clear()
        {
            Length = 0;
            Position = 0;
        }
        /// <summary>
        /// Get will return the byte at the current Position and
        /// increment the Position.
        /// </summary>
        /// <returns></returns>
        public byte Get()
        {
            byte v = ReadableBytes[Position];
            Position = Position + 1;
            return v;
        }
        public byte Get(int index)
        {
            return (byte)ReadableBytes[index];
        }
        /// <summary>
        /// Duplicate will create a new ByteBuffer using the same
        /// byte[] array.
        /// </summary>
        /// <returns></returns>
        public ByteBuffer Duplicate()
        {
            ByteBuffer dupe = new ByteBuffer(ReadableBytes);
            dupe.Position = Position;
            dupe.Length = Length;
            return dupe;
        }
        public ByteBuffer Slice()
        {
            byte[] bytes = new byte[Length - Position];
            Array.Copy(ReadableBytes, Position, bytes, 0, Length - Position);
            ByteBuffer slice = new ByteBuffer(bytes);
            return slice;
        }
        public void WriteByte(byte value)
        {
            ReadableBytes[Position] = value;
            Position++;
        }
        protected int Compare(byte x, byte y)
        {
            return x.CompareTo(y);
        }
        public int CompareTo(ByteBuffer other)
        {
            long n = Position + Math.Min(Remaining(), other.Remaining());
            if (Position > int.MaxValue || other.Position > int.MaxValue)
            {
                throw new IndexOutOfRangeException("Position value is greater than maximum int value");
            }
            for (int i = (int)Position, j = (int)other.Position; i < n; i++, j++)
            {
                int cmp = Compare(Get(i), other.Get(j));
                if (cmp != 0)
                {
                    return cmp;
                }
            }
            return (int)(Remaining() - other.Remaining());
        }
        public override int GetHashCode()
        {
            if (HashCode == 0)
            {
                HashCode = 1;
                long p = Position;
                for (long i = Length - 1; i >= p; i--)
                {
                    HashCode = 31 * HashCode + (int)Get(Convert.ToInt32(i));
                }
            }
            return HashCode;
        }
        public override bool Equals(object obj)
        {
            if (obj is ByteBuffer)
            {
                return 0 == CompareTo((ByteBuffer)obj);
            }
            else
            {
                return false;
            }
        }
    }
}
